package com.csx.governance.traffic.adapter.outbound;

import com.csx.governance.traffic.core.context.TrafficContextHolder;
import com.csx.governance.traffic.core.context.TrafficInternalContexts;
import com.csx.governance.traffic.core.context.TrafficMessage;
import com.csx.governance.traffic.core.context.TrafficRequest;
import com.csx.governance.traffic.route.Filter;
import com.csx.governance.traffic.route.FilterChain;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.DefaultRequestContext;
import org.springframework.cloud.client.loadbalancer.Request;
import org.springframework.cloud.loadbalancer.core.DelegatingServiceInstanceListSupplier;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import reactor.core.publisher.Flux;

import java.util.List;

/**
 * 负载均衡器的 服务实例提供器扩展 - 适配 Spring Cloud Loadbalancer.
 * <p> 将原生的实例列表通过自定义的FilterChain中一系列实例过滤器过滤,得到过滤后的实例列表 </p>
 * @author cuisongxu
 * @date 2023/3/18 18:39
 */
public class ServiceInstanceSupplierLoadBalancerExt extends DelegatingServiceInstanceListSupplier {

    private final FilterChain<ServiceInstance> filterChain;

    public ServiceInstanceSupplierLoadBalancerExt(ServiceInstanceListSupplier delegate, List<Filter> filters) {
        super(delegate);
        this.filterChain = new FilterChain<>(ServiceInstance.class,
                ServiceInstance::getMetadata,
                filters,
                this::formatServer);
    }

    @Override
    public Flux<List<ServiceInstance>> get() {
        TrafficMessage trafficRequest;
        if(TrafficContextHolder.getContext() == null) {
            // 外调请求是由程序内部定时任务发起的
            trafficRequest = TrafficInternalContexts.get();
        }else {
            trafficRequest = TrafficContextHolder.getContext().getSubRequest();
        }
        Flux<List<ServiceInstance>> serviceInstancesFlux = getDelegate().get();
        return serviceInstancesFlux.map(x -> filtered(x, trafficRequest));
    }

    private List<ServiceInstance> filtered(List<ServiceInstance> serviceInstances, TrafficMessage request) {
        return filterChain.filter(serviceInstances, request);
    }

    @Override
    public Flux<List<ServiceInstance>> get(Request request) {
        Object context = request.getContext();
        TrafficMessage trafficRequest;
        if (context instanceof DefaultRequestContext) {
            Object clientRequest = ((DefaultRequestContext) context).getClientRequest();
            if(clientRequest instanceof TrafficLoadBalancerRequest) {
                trafficRequest = ((TrafficLoadBalancerRequest<?>) clientRequest).getRequest();
            }else if(clientRequest instanceof TrafficMessage) {
                trafficRequest = (TrafficMessage) clientRequest;
            }else {
                if(TrafficContextHolder.getContext() != null) {
                    trafficRequest = TrafficContextHolder.getContext().getSubRequest();
                }else {
                    trafficRequest = TrafficInternalContexts.get();
                }
            }
        } else {
            trafficRequest = TrafficContextHolder.getContext().getSubRequest();
        }
        Flux<List<ServiceInstance>> serviceInstancesFlux = getDelegate().get(request);
        return serviceInstancesFlux.map(list -> filtered(list, trafficRequest));
    }

    /**
     * 格式化服务列表用于debug打印. 通常ServiceInstance对应的实现并没有重写toString导致打印信息不可读
     * @param serviceInstance 服务实例
     * @return 格式化后的服务实例打印信息
     */
    private String formatServer(ServiceInstance serviceInstance) {
        StringBuilder sb = new StringBuilder();
        sb.setLength(0);
        sb.append(serviceInstance.getHost())
                .append(":")
                .append(serviceInstance.getPort())
                .append(", metadata:")
                .append(serviceInstance.getMetadata().toString());
        return sb.toString();
    }
}
